package mg.Pong;

import utils.DBHandler;
import utils.ScoreGUIHandler;
import utils.ScoreGUIHandler.GameScoreType;
import globalObjects.Player;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jmex.game.state.BasicGameState;
import com.jmex.game.state.GameStateManager;
import java.util.Random;

public class Pong extends BasicGameState {	
	//Init the db handler
	DBHandler db = new DBHandler();
	
	//Init the random generator
	Random rgen = new Random();
	
	//Setup the speed of all boards
	float boardSpeed = .3f;
	
	//Initiate player
	Player player = new Player(boardSpeed);
	Node playerBoard = player.getPlayer(8, 1, 1);
	
	//Initiate AI pong paddle
	Box AI = new Box("AI", new Vector3f(), 8 ,1, 1);
	float xAITarg = 0;
	float AIDifficulty = 20; 	// The higher, the easier (it's the distance it can be out by)
	
	//Initiate the ball
	Sphere pongBall = new Sphere("Pong ball", new Vector3f(), 6, 6, 6);
	float ballRandomness = 0.03f;
	float xBallVel = .3f;
	float zBallVel = .35f;
	
	
	//Initate the wall node
	Node walls = new Node();
	Node scoreWalls = new Node();
	
	//score for AI & Player
	int playerScore = 0;
	int aiScore = 0;
	ScoreGUIHandler GUI = new ScoreGUIHandler(GameScoreType.OnePlayerGame);

	// Main constuctor
	public Pong() {
		//Set the window name
		super("tlcpong");
		
		//Init player controls
		player.initXControls();
		
		//Create the play field
		rootNode.attachChild(getPlayField());
		
		//Configure the AI and playerBoard
		AI.setModelBound(new BoundingBox());
		AI.setDefaultColor(ColorRGBA.red);
		AI.getLocalTranslation().setZ(-60);
		
		playerBoard.setModelBound(new BoundingBox());
		playerBoard.getLocalTranslation().setZ(60);
		
		//Attach the player and AI to the root node
		rootNode.attachChild(playerBoard);
		rootNode.attachChild(AI);
		
		//Update the root node.
		rootNode.updateModelBound();
		rootNode.updateRenderState();
		rootNode.updateGeometricState(0,true);
		
		//Attach the score gui
		GameStateManager.getInstance().attachChild(GUI);
		GUI.setActive(true);
	}
	
	/**
	 * This returns a built play field with everything that isn't a scoring item
	 * @return play field
	 */
	private Node getPlayField() {
		Node playField = new Node();
		
		Box leftWall = new Box("left wall", new Vector3f(), 2,4, 65);
		leftWall.getLocalTranslation().setX(-42);
		leftWall.setModelBound(new BoundingBox());
		leftWall.setDefaultColor(ColorRGBA.green);
		walls.attachChild(leftWall);
		
		Box rightWall = new Box("right wall", new Vector3f(),2,4,65);
		rightWall.getLocalTranslation().setX(42);
		rightWall.setModelBound(new BoundingBox());
		rightWall.setDefaultColor(ColorRGBA.green);
		walls.attachChild(rightWall);
		
		Box playerScoreWall = new Box("Player score wall", new Vector3f(), 44,4,2);
		playerScoreWall.getLocalTranslation().setZ(67);
		playerScoreWall.setModelBound(new BoundingBox());
		playerScoreWall.setDefaultColor(ColorRGBA.green);
		scoreWalls.attachChild(playerScoreWall);
		
		Box aiScoreWall = new Box("AI score wall", new Vector3f(), 44,4,2);
		aiScoreWall.getLocalTranslation().setZ(-67);
		aiScoreWall.setModelBound(new BoundingBox());
		aiScoreWall.setDefaultColor(ColorRGBA.green);
		scoreWalls.attachChild(aiScoreWall);
		
		playField.attachChild(scoreWalls);
		playField.attachChild(walls);
		
		pongBall.setLocalScale(.25f);
		pongBall.setModelBound(new BoundingSphere());
		pongBall.setDefaultColor(ColorRGBA.cyan);
		playField.attachChild(pongBall);
		
		return playField;
	}
	
	// Update position of AI player (for now it just moves back and forth)
	private void AIUpdate(){
		// Get current position of AI
		float x = AI.getLocalTranslation().x;
		float z = AI.getLocalTranslation().z;

		// If ball has collided with something, and is coming towards AI
		if(zBallVel < 0 && 
				(walls.getChild(0).hasCollision(pongBall, false) ||
				walls.getChild(1).hasCollision(pongBall, false) ||
				playerBoard.hasCollision(pongBall, false)) ||
				scoreWalls.getChild(1).hasCollision(pongBall, false)){
		
			// Work out X value of where ball will end up and set as target
			// y - b = m(x - a)
			// x = (y-b)/m + a;
			xAITarg = (z - pongBall.getLocalTranslation().z)/(zBallVel/xBallVel) + pongBall.getLocalTranslation().x;
			
			// Add randomness
			xAITarg += (rgen.nextFloat()*AIDifficulty*2) - (AIDifficulty);
		}
		// Move Ball
		
		// If current position higher than target
		if(x > xAITarg) {
			// Move down
			AI.getLocalTranslation().setX(x - boardSpeed);
		}
		
		// If current position lower than target
		else if(x < xAITarg) {
			// Move up
			AI.getLocalTranslation().setX(x + boardSpeed);
		}
		
		// Make sure ball isn't outside game area
		if(AI.getLocalTranslation().x > 30) {
			AI.getLocalTranslation().setX(30);
		}
		else if(AI.getLocalTranslation().x < -30) {
			AI.getLocalTranslation().setX(-30);
		}
	}

	public void update(float tpf) {
		//Handle player controls
		player.handleControls();
		keepPlayerInBounds();
		
		//Move the AI's pong paddle 
		AIUpdate();
		
		//Update root node's location on items
		rootNode.updateGeometricState(tpf, true);
		
		//Update the score
		//updateScore();
		
		//Handle the sphere
		handleSphere();
		
		//End game if needed
		checkEndGame();
	}

	private void checkEndGame() {
		//The player won
		if (playerScore == 15){
			db.addCoin(playerScore);
			setActive(false);
		}
		//the ai won
		else if (aiScore == 15){
			System.out.println("you loose");
			setActive(false);
		}
		
	}

	// Make sure player hasn't left game area
	private void keepPlayerInBounds() {
		// Get location of player
		float x = playerBoard.getLocalTranslation().getX();
		
		// Move player back inside if neccesary
		if (x > 30){
			playerBoard.getLocalTranslation().setX(30);
		}
		else if (x < -30){
			playerBoard.getLocalTranslation().setX(-30);
		}
	}
	
	// Handle Sphere movements
	private void handleSphere() {
		// Get sphere location
		float x = pongBall.getLocalTranslation().x;
		float z = pongBall.getLocalTranslation().z;
		
		// Bounce sphere if at edges
		if(walls.getChild(0).hasCollision(pongBall, false) ||
				walls.getChild(1).hasCollision(pongBall, false)){
			xBallVel *= -1f;
			addBallRandom();
		}
		
		// Bounce if collided with either paddle
		if(AI.hasCollision(pongBall, false) ||
				playerBoard.hasCollision(pongBall, false)){
			zBallVel *= -1f;
			addBallRandom();
		}
		
		//Reset ball and update score if collided with score walls
		//This is the players score board
		if(scoreWalls.getChild(0).hasCollision(pongBall, false)){
			x = 0;
			z = 0;
			addBallRandom();
			aiScore += 1;
			GUI.setPlayer2Score(aiScore);
		//This is the AI's score board
		} else if(scoreWalls.getChild(1).hasCollision(pongBall, false)){
			x = 0;
			z = 0;
			addBallRandom();
			playerScore += 1;
			GUI.setPlayerScore(playerScore);
		}
		
		// Update sphere location
		pongBall.getLocalTranslation().setX(x + xBallVel);
		pongBall.getLocalTranslation().setZ(z + zBallVel);
	}
	
	private void addBallRandom(){
		xBallVel += (rgen.nextFloat()*ballRandomness*2 - ballRandomness);
		zBallVel += (rgen.nextFloat()*ballRandomness*2 - ballRandomness);
	}
}

